﻿using System;
using System.Drawing;
using System.Collections.Generic;
//using Microsoft.DirectX;
using System.IO;

namespace System
{
	public struct V2D
	{
		public double X, Y;
		public bool Valid;
		public static V2D Null{get{return new V2D(double.NaN,double.NaN);}}
		//Constructors
		public V2D(double x, double y)
		{
			X = x;
			Y = y;
			Valid = !double.IsNaN(x) && !double.IsNaN(y) ;
		}
		public V2D(double[] V)
			: this()
		{
			if (V.Length > 1)
			{
				X = V[0];
				Y = V[1];
				Valid = true;
			}
			else
			{
				X = Y = double.NaN;
				Valid = false;
			}
		}
		public V2D(String S)
		{
			String[] C = S.Split(';');
			try
			{
				X = double.Parse(C[0]);
				Y = double.Parse(C[1]);
				Valid = true;
			}
			catch
			{
				X = Y  = double.NaN;
				Valid = false;
			}
		}
		public static void Swap(ref V2D A, ref V2D B)
		{
			V2D Temp = A;
			A = B;
			B = Temp;
		}

		public static int CompareXY(V2D P1, V2D P2)
		{
			if (P1.X < P2.X)
				return -1;
			else if (P1.X > P2.X)
				return 1;
			else if (P1.Y < P2.Y)
				return -1;
			else if (P1.Y > P2.Y)
				return 1;
			else
				return 0;
		}
		public static bool operator <(V2D P1, V2D P2)
		{
			return CompareXY(P1, P2) == -1;
		}
		public static bool operator >(V2D P1, V2D P2)
		{
			return CompareXY(P1, P2) == 1;
		}
		public static bool operator <=(V2D P1, V2D P2)
		{
			return CompareXY(P1, P2) < 1;
		}
		public static bool operator >=(V2D P1, V2D P2)
		{
			return CompareXY(P1, P2) > -1;
		}
		public override int GetHashCode()
		{
			return ((IntPtr)X).ToInt32();
		}
		public override bool Equals(object obj)
		{
			if (obj is V2D)
				return (V2D)obj == this;
			else
				return false;
		}
		public static bool operator ==(V2D v1, V2D v2)
		{
			double precision = 1e-12;
			if (object.ReferenceEquals(v1, v2))
				return true;
			else if ((object)v1 == null || (object)v2 == null)
				return false;
			else
				return
					((v1.X == 0 && v2.X == 0) || (Math.Abs(v1.X - v2.X) / Math.Max(Math.Abs(v1.X), Math.Abs(v2.X)) < precision))
					&& ((v1.Y == 0 && v2.Y == 0) || (Math.Abs(v1.Y - v2.Y) / Math.Max(Math.Abs(v1.Y), Math.Abs(v2.Y)) < precision));
		}
		public static bool operator !=(V2D v1, V2D v2)
		{
			return !(v1 == v2);
		}
		public static V2D operator +(V2D v1, V2D v2)
		{
			if (v1.Valid && v2.Valid)
				return new V2D(v1.X + v2.X, v1.Y + v2.Y);
			else
				return V2D.Null;
		}
		public static V2D operator -(V2D v1, V2D v2)
		{
			if (v1.Valid && v2.Valid)
				return new V2D(v1.X - v2.X, v1.Y - v2.Y);
			else
				return V2D.Null;
		}
		public static V2D operator -(V2D v1)
		{
			if (v1.Valid)
				return new V2D(-v1.X, -v1.Y);
			else
				return V2D.Null;
		}
		//public static V2D operator /(V2D v1, double n)
		public static double operator *(V2D v1, V2D v2)
		{
			if (v1.Valid && v2.Valid)
				return (v1.X * v2.X) + (v1.Y * v2.Y) ;
			else
				return double.NaN;
		}
		public static V2D operator *(V2D v1, double n)
		{
			if (v1.Valid)
				return new V2D(v1.X * n, v1.Y * n);
			else
				return V2D.Null;
		}
		public static V2D operator *(double n, V2D v1)
		{
			return v1 * n;
		}
		//public static V2D operator %(V2D v1, V2D v2)

		//Conversion
		public override String ToString()
		{
			if (Valid)
				return X.ToString() + ";" + Y.ToString() ;
			else
				return "<<Invalid>>";
		}
		public String ToString(int i, int j)
		{
			int a;
			String format, result;
			format = "{0:";
			for (a = 0; a < i; a++)
				format += "0";
			if (j > 0)
			{
				format += ".";
				for (a = 0; a < j; a++)
					format += "0";
			}
			format += "}";
			result = '(' + String.Format(format, X);
			result += ';' + String.Format(format, Y)+')';
			return result;
		}
		public double[] ToArray()
		{
			return new double[] { X, Y};
		}
		public double Length
		{
			get
			{
				return Math.Sqrt(LengthSq);
			}
		}
		public double LengthSq
		{
			get
			{
				return X * X + Y * Y ;
			}
		}
		public V2D Unit
		{
			get
			{
				return this * (1.0d / this.Length);
			}
			set
			{
				this = value;
				double d = this.Length;
				this.X = this.X / d;
				this.Y = this.Y / d;
			}
		}
		// Math
		public static double AngleCos(V2D v1, V2D v2)
		{
			return (v1 * v2) / (v1.Length * v2.Length);
		}
		public static double Angle(V2D v1, V2D v2)
		{
			double x = (v1 * v2) / (v1.Length * v2.Length);
			if (x >= 1)
				return 0;
			else if (x <= -1)
				return Math.PI;
			else
				return Math.Acos(x);
		}
		public static double AngleD(V2D v1, V2D v2)
		{
			return Angle(v1, v2) / Math.PI * 180;
		}
		public V2D ParallelPart(V2D To)
		{
			return To.Unit * Length * AngleCos(this, To);
		}
		public V2D Tangential(V2D To)
		{
			return Unit * To.Length *(1/ AngleCos(this, To));
		}
		public V2D CrossPart(V2D To)
		{
			return this - ParallelPart(To);
		}
		public V2D Interpolate(V2D v1, double n)
		{
			return this * (1 - n) + v1 * n;
		}
		public static V2D Interpolate(V2D v1, V2D v2, double n)
		{
			return v1 * (1 - n) + v2 * n;
		}

		public PointF ToPointF()
		{
			return new PointF((float)X, (float)Y);
		}
		public Point ToPoint()
		{
			return new Point((int)X, (int)Y);
		}

		public V2D(byte[] msg, ref int msgptr)
		{
			X = BitConverter.ToDouble(msg, msgptr);
			Y = BitConverter.ToDouble(msg, msgptr + 8);
			Valid = true;
			msgptr += 16;
		}
		public void Append(List<byte> Res)
		{
			Res.AddRange(BitConverter.GetBytes(X));
			Res.AddRange(BitConverter.GetBytes(Y));
		}

		public void Save(BinaryWriter W)
		{
			W.Write(X);
			W.Write(Y);
		}
		public void Load(BinaryReader R)
		{
			X = R.ReadDouble();
			Y = R.ReadDouble();
			Valid = true;
		}

		public V2D(BinaryReader R)
		{
			X = R.ReadDouble();
			Y = R.ReadDouble();
			Valid = true;
		}
	}
}